package com.example.onlinemonitoring.AOP;

import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import com.example.onlinemonitoring.model.common.MyResult;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

/**
 * RepeatSubmitAspect
 * 防止页面重复提交的aop，若1秒内重复提交，则返回空值
 * @author little pig page
 * @date 2021-11-04
 */
@Aspect
@Component
public class RepeatSubmitAspect {

    // 创建一个缓存实例，用于存储请求的token和路径信息
    private static final Cache<String,Object> CACHE = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.SECONDS).build();

    // 定义切入点，匹配带有 @RepeatSubmit 注解的方法
    @Pointcut("@annotation(RepeatSubmit)")
    public void pointCut(RepeatSubmit RepeatSubmit) {
    }

    // 环绕通知，在切入点方法执行前后进行处理
    @Around("pointCut(RepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp,RepeatSubmit RepeatSubmit) throws Throwable {
        // 获取当前请求的上下文信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 将请求转换为 HttpServletRequest 对象
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        // 获取请求的 token（或者 JSessionId）
        String token = request.getHeader("Authorization");
        // 获取请求的路径
        String path = request.getServletPath();
        // 生成用于缓存的唯一键值
        String key = token + path;

        // 如果键值有效
        if(StringUtils.hasText(key)){
            // 如果缓存中存在该键值，表示重复提交
            if(CACHE.getIfPresent(key) != null){
                // 返回一个自定义结果对象，表示重复提交
                return new MyResult<Integer>(MyResult.FALSE,"请勿重复提交！");
            }
            // 将键值存入缓存
            CACHE.put(key,key);
        }

        try{
            // 执行切入点方法
            return pjp.proceed();
        }catch (Throwable throwable){
            // 抛出运行时异常，表示防重校验异常
            throw new RuntimeException("防重校验异常！");
        }finally {
            // 可以在此处进行一些清理操作
        }
    }
}
